package parserjc

import ewu._
import ewu.anweisungen._
import ewu.expressions._
import java.util.ArrayList
import parserjc.jaccie.SICTree
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.HashMap

/**
 * Die Klasse Evaluator erzeugt aus einem uebergebenem SICTree eine entsprechende
 * Konfiguration in EWU. Weitere fuer die weitere Verarbeitung relevante Daten,
 * wie zum Beispiel Variablen werden hier bereits herausgefiltert und in speziellen
 * Variablen abgelegt
 */

class Evaluator(tree: SICTree) {
  var conf:HashMap[String, Konfiguration] = new HashMap();
  var functionExecutes:HashMap[String, ArrayList[functionExpression]] = new HashMap();
  var allvars:HashMap[String, HashMap[Int, String]] = new HashMap();
  var inputvars:HashMap[String, Environment] = new HashMap();
  var outputvars:HashMap[String, Environment] = new HashMap();
  var varsrelateoninput:HashMap[String, HashMap[Name, Expression]] = new HashMap();
  var arrayvars:HashMap[String, ArrayList[String]] = new HashMap();
  var hauptprozedurname = ""
  
  def getInputVars(): (String, Environment) = {
    if(inputvars.size == 0)
      return null
    else
      return inputvars.get(hauptprozedurname) match {
        case Some(x) => (hauptprozedurname, x)
        case None => null
      };    
  }
  
  def getOutputVars(): (String, Environment) = {
    if(outputvars.size == 0)
      return null
    else
      return outputvars.get(hauptprozedurname) match {
        case Some(x) => (hauptprozedurname, x)
        case None => null
      };    
  }
  
  def getVarsDependInput(): HashMap[Name, Expression] = {
    if(varsrelateoninput.size == 0)
      return null
    else
      return varsrelateoninput.get(hauptprozedurname) match {
        case Some(x) => x
        case None => null
      };    
  }
  
  /*
   * Gibt die Konfigurationen von in einem Programm vorhandenen
   * Prozeduren in Verbindung mit dem Prozedurnamen in einer
   * HashMap zurueck
   */
  def getEWU(): HashMap[String, Konfiguration] = {
    if(tree != null)
      programmToEWU(tree);
    return conf;
  }

  def programmToEWU(tree: SICTree) : Unit = {
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {
        case x:SICTree => x;
        case _ => null
      }
      if(son.getValue().equals("HauptProzedur"))
        hauptProzedurToEWU(son);
      else if(son.getValue().equals("PackageBody"))
        packageBodyToEWU(son);
    }
    
    for(i <- conf) {
      /*
       * functionExpressions durchlaufen, und Konfigurationen/Vars 
       * der zugehoerigen Prozeduren hinzufuegen 
       */
      var tfuncs = functionExecutes.get(i._1) match {
        case Some(x) => x
        case None => null
      }
      if(tfuncs != null) {
        for(j <- 0 to tfuncs.size()-1) {
          var tfunc = tfuncs.get(j)
          tfunc.funcconf = conf.get(tfunc.name) match {
            case Some(x) => x
            case None => null
          }
          if(tfunc.funcconf != null)
            tfunc.initconf = new Konfiguration(new Environment().addValues(
                tfunc.funcconf.umgebung), tfunc.funcconf.teil)
          
          tfunc.allvars = allvars.get(tfunc.name) match {
            case Some(x) => x
            case None => null
          }
          
          tfunc.inputvars = inputvars.get(tfunc.name) match {
            case Some(x) => x
            case None => null
          }
          
          tfunc.outputvars = outputvars.get(tfunc.name) match {
            case Some(x) => x
            case None => null
          }
          
          tfunc.varsrelateoninput = varsrelateoninput.get(tfunc.name) match {
            case Some(x) => x
            case None => null
          }
          
          tfunc.arrayvars = arrayvars.get(tfunc.name) match {
            case Some(x) => x
            case None => null
          }
        }
      }
    }
  }

  def hauptProzedurToEWU(tree: SICTree) : Unit = {
    var name = ""
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Prozedur"))
        name = prozedurToString(son);
    }

    hauptprozedurname = name
  }
  
  def packageBodyToEWU(tree: SICTree) : Unit = {
    var name = ""
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier")) {
        name = identifierToString(son);
        if(!inputvars.contains(name))
          inputvars.put(name, new Environment());
        if(!outputvars.contains(name))
          outputvars.put(name, new Environment());
        if(!allvars.contains(name))
          allvars.put(name, new HashMap);
        if(!functionExecutes.contains(name))
          functionExecutes.put(name, new ArrayList);
        if(!arrayvars.contains(name))
          arrayvars.put(name, new ArrayList);
      }
        
      if(son.getValue().equals("ProzedurDeklarationen"))
        prozedurDeklarationenToEWU(son, name)
    }
    
    hauptprozedurname = name
  }
  
  def prozedurDeklarationenToEWU(tree: SICTree, prozedurname: String) : Unit = {
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }
        
      if(son.getValue().equals("DeklarationsBereich"))
        deklarationsBereichToEnvironment(son, prozedurname)
    }
  }

  def prozedurToString(tree: SICTree) : String = {
    var prozedurname:String = null;
    var env:Environment = null;
    var anwf:Anweisungsfolge = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }
      //Prozedurnamen abfangen
      if(son.getValue().equals("Identifier")) {
        prozedurname = identifierToString(son);
        if(!inputvars.contains(prozedurname))
          inputvars.put(prozedurname, new Environment());
        if(!outputvars.contains(prozedurname))
          outputvars.put(prozedurname, new Environment());
        if(!allvars.contains(prozedurname))
          allvars.put(prozedurname, new HashMap());
        if(!functionExecutes.contains(prozedurname))
          functionExecutes.put(prozedurname, new ArrayList);
        if(!arrayvars.contains(prozedurname))
          arrayvars.put(prozedurname, new ArrayList);
      }       
      else if(son.getValue().equals("ProzedurOptionals")) {
        env = prozedurOptionalsToEnvironment(son, prozedurname);
      }
      else if(son.getValue().equals("Anwfolge"))
        anwf = anwfolgeToAnweisungsfolge(son, prozedurname);
    }

    conf.put(prozedurname, new Konfiguration(env, anwf));
    return prozedurname
  }

  def prozedurOptionalsToEnvironment(tree: SICTree, prozedurname: String) : Environment = {
    var ret:Environment = new Environment();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Parameter"))
        ret.addValues(parameterToEnvironment(son, prozedurname));
      else if(son.getValue().equals("DeklarationsBereich"))
        ret.addValues(deklarationsBereichToEnvironment(son, prozedurname));
    }

    return ret;
  }

  def parameterToEnvironment(tree: SICTree, prozedurname: String) : Environment = {
    var ret:Environment = new Environment();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ParameterDef"))
        ret.addValues(parameterDefToEnvironment(son, prozedurname));
      else if(son.getValue().equals("Parameter"))
        ret.addValues(parameterToEnvironment(son, prozedurname));
    }

    return ret;
  }

  def parameterDefToEnvironment(tree: SICTree, prozedurname: String) : Environment = {
    var ret:Environment = new Environment();
    var parts:ArrayList[String] = new ArrayList();
    var init:Expression = Number(BigInt(0));
    var input:Boolean = false;
    var output:Boolean = false
    var isArray:Boolean = false
    var range:ValueRange = null
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("IdentifierList"))
        parts = identifierListToArrayList(son);
      else if(son.getValue().equals("ParameterMode")) {
        input = parameterModeIsIn(son);
        output = parameterModeIsOut(son);
      }
      else if(son.getValue().equals("TypeAngabe")) {
        isArray = typeAngabeIsArray(son)
        if(isArray)
          range = typeAngabeToValueRange(son, prozedurname)
      }
      else if(son.getValue().equals("TypeInitialisierung")) {
        init = typeInitToExpression(son, prozedurname)
      }
    }
    if(isArray && range != null) {
      if(!init.isInstanceOf[ExpArray])
          init = new ExpArray(range.fromE, range.toE)
      init.asInstanceOf[ExpArray].setExpRange(range.fromE, range.toE)
    }      
    for(i <- 0 to parts.size()-1) {
      if(isArray)
        arrayvars.get(prozedurname) match {
          case Some(x) => x.add(parts.get(i))
          case None => null
        }
      allvars.get(prozedurname) match {
        case Some(x) => 
          var tc = 0
          while(x.get(tc).isInstanceOf[Some[String]])
            tc += 1
          x.put(tc, parts.get(i))
        case None => null
      }

      if(input == true) {
        inputvars.get(prozedurname) match {
          case Some(x) => x.addValue(new Name(parts.get(i)), init)
          case None => null
        }
      }
      if(output == true) {
        outputvars.get(prozedurname) match {
          case Some(x) => x.addValue(new Name(parts.get(i)), init)
          case None => null
        }
      }
      ret.addValue(new Name(parts.get(i)), init);
    }
    return ret;
  }
  
  def typeAngabeIsArray(tree:SICTree) : Boolean = {
    var ret:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ArrayType"))
        ret = true;
    }
    return ret;
  }
  
  def typeAngabeToValueRange(tree:SICTree, prozedurname: String) : ValueRange = {
    var ret:ValueRange = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ArrayType")) {
        var nextsons = son.getSons();
        for(j <- 0 to nextsons.size()-1) {
          nextson = nextsons.elementAt(j) match {         
            case x:SICTree => x;         
            case _ => null       
          }
          
          if(nextson.getValue().equals("ArrayDefBereich"))
            ret = arrayDefBereichToValueRange(nextson, prozedurname)
        }
      }
    }
    return ret;
  }
  
  def arrayDefBereichToValueRange(tree: SICTree, prozedurname: String) : ValueRange = {
    var ret:ValueRange = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ArrayRange"))
        ret = arrayRangeToValueRange(son, prozedurname)
    }
    return ret;
  }
  
  def arrayRangeToValueRange(tree: SICTree, prozedurname: String) : ValueRange = {
    var parts:ArrayList[Expression] = new ArrayList();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ExpWert"))
        parts.add(expWertToExpression(son, prozedurname))
    }
    return new ValueRange(parts.get(0), parts.get(1));
  }
  
  def typeInitToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var ret:Expression = new Number(BigInt(0));
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("AllWert"))
        ret = allWertToExpression(son, prozedurname)
    }

    return ret;
  }

  def parameterModeIsIn(tree: SICTree) : Boolean = {
    var ret:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("inP"))
        ret = true;
    }

    return ret;
  }
  
  def parameterModeIsOut(tree: SICTree) : Boolean = {
    var ret:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("outP"))
        ret = true;
    }
    
    return ret;
  }

  def deklarationsBereichToEnvironment(tree: SICTree, prozedurname: String) : Environment = {
    var ret:Environment = new Environment();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Deklaration"))
        ret.addValues(deklarationToEnvironment(son, prozedurname));
      else if(son.getValue().equals("DeklarationsBereich"))
        ret.addValues(deklarationsBereichToEnvironment(son, prozedurname));
    }

    return ret;
  }

  def deklarationToEnvironment(tree: SICTree, prozedurname: String) : Environment = {
      var ret:Environment = new Environment();
      var parts:ArrayList[String] = new ArrayList();
      var setto:BigInt = BigInt(0);
      var settoExp:Expression = new Number(setto);
      var isArray: Boolean = false;
      var range:ValueRange = null;
      var sons = tree.getSons();

      var son, nextson, tmpson : SICTree = null;
      for(i <- 0 to sons.size()-1) {
        son = sons.elementAt(i) match {         
          case x:SICTree => x;         
          case _ => null       
        }

        if(son.getValue().equals("Prozedur"))
          prozedurToString(son);
        else if(son.getValue().equals("IdentifierList"))
          parts = identifierListToArrayList(son);
        else if(son.getValue().equals("TypeAngabe")) {
          isArray = typeAngabeIsArray(son)
          if(isArray)
            range = typeAngabeToValueRange(son, prozedurname)
        }
        else if(son.getValue().equals("TypeInitialisierung")) {
          settoExp = typeInitToExpression(son, prozedurname)
        }
      }
      if(isArray && range != null && !settoExp.isInstanceOf[Name]) {
        if(!settoExp.isInstanceOf[ExpArray])
          settoExp = new ExpArray(range.fromE, range.toE)
        settoExp.asInstanceOf[ExpArray].setExpRange(range.fromE, range.toE)
      }

      for(i <- 0 to parts.size()-1) {
        if(isArray)
          arrayvars.get(prozedurname) match {
            case Some(x) => x.add(parts.get(i))
            case None => null
          }
        if(settoExp.isInstanceOf[ewu.Number] || settoExp.isInstanceOf[ExpArray]) {
          ret.addValue(new Name(parts.get(i)), settoExp);
        }
        else {
          if(settoExp != null) {
              var tmpvars = varsrelateoninput.get(prozedurname) match {
              case Some(x) => x
              case None => null
            }
            if(tmpvars == null) {
              tmpvars = new HashMap[Name, Expression]()
              varsrelateoninput.put(prozedurname, tmpvars)
            }
            tmpvars.put(new Name(parts.get(i)), settoExp)
          }       
          ret.addValue(new Name(parts.get(i)), new ewu.Number(setto));
        }
         
      }
              
      return ret;
  }

  def anwfolgeToAnweisungsfolge(tree: SICTree, prozedurname: String) : Anweisungsfolge = {
    var anwf:Anweisungsfolge = new Anweisungsfolge(new ArrayBuffer());
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }
      
      if(son.getValue().equals("Anw"))
        anwf.addAnweisung(anwToAnweisung(son, prozedurname));
      else if(son.getValue().equals("Anwfolge"))
        anwf.addAnweisungen(anwfolgeToAnweisungsfolge(son, prozedurname));
    }
    return anwf;
  }

  def anwToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
       son = sons.elementAt(i) match {         
         case x:SICTree => x;         
         case _ => null       
       }

       if(son.getValue().equals("Zusicherung"))
           return zusicherungToAnweisung(son, prozedurname);
       else if(son.getValue().equals("AZuweisung"))
           return aZuweisungToAnweisung(son, prozedurname);
       else if(son.getValue().equals("AArrayZuweisung"))
           return aArrayZuweisungToAnweisung(son, prozedurname);
       else if(son.getValue().equals("ASchleife"))
           return aSchleifeToAnweisung(son, prozedurname);
       else if(son.getValue().equals("AFall"))
           return aFallToAnweisung(son, prozedurname);
       else if(son.getValue().equals("AProzedurAufruf"))
           return aProzedurAufrufToAnweisung(son, prozedurname);
    }
    return null;
  }

  def zusicherungToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var zname:String = "";
    var exp:BoolExpression = null;
    var sons = tree.getSons();
    var boundexp:Expression = null

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier"))
        zname = identifierToString(son);
      else if(son.getValue().equals("Bedingungen"))
        exp = bedingungenToBoolExpression(son, prozedurname);
      else if(son.getValue().equals("Ausdruck"))
        boundexp = ausdruckToExpression(son, prozedurname);
    }
    if(zname.toLowerCase.startsWith("bound") && boundexp != null)
      return new Bound(zname.replaceFirst("(?i)bound", ""), boundexp);
    else if(exp != null)
      return new Assertion(zname, exp);
    else
      throw new Exception("Fehlerhafte Zusicherung: " + zname)
  }

  def aZuweisungToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var name:String = "";
    var exp:Expression = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier"))
        name = identifierToString(son);
      else if(son.getValue().equals("Ausdruck"))
        exp = ausdruckToExpression(son, prozedurname);
    }
    return new Zuweisung(name, exp);
  }
  
  def aArrayZuweisungToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var name:String = "";
    var exp:Expression = null;
    var pars:Expression = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ProzedurAufruf")) {
        var functmp:ArrayAtIndex = prozedurAufrufToExpression(son, prozedurname) match {
          case x:ArrayAtIndex => x
          case _ => null
        }
        name = functmp.name
        pars = functmp.index
      }
      else if(son.getValue().equals("Ausdruck"))
        exp = ausdruckToExpression(son, prozedurname);
    }
    return new ArrayZuweisung(name, pars, exp);
  }

  def aSchleifeToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var exp:BoolExpression = null;
    var anwf:Anweisungsfolge = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Bedingungen"))
        exp = bedingungenToBoolExpression(son, prozedurname);
      else if(son.getValue().equals("Anwfolge"))
        anwf = anwfolgeToAnweisungsfolge(son, prozedurname);
    }
    return  new Schleife(exp, anwf);
  }

  def aFallToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var exp:BoolExpression = null;
    var parts:ArrayList[Anweisungsfolge] = new ArrayList();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Bedingungen"))
        exp = bedingungenToBoolExpression(son, prozedurname);
      else if(son.getValue().equals("Anwfolge"))
        parts.add(anwfolgeToAnweisungsfolge(son, prozedurname));
    }

    if(parts.size() == 1)
      return Fall(exp, parts.get(0), new Anweisungsfolge(new ArrayBuffer()));
    else
      return Fall(exp, parts.get(0), parts.get(1));
  }
  
  def aProzedurAufrufToAnweisung(tree: SICTree, prozedurname: String) : Anweisung = {
    var sons = tree.getSons();
    var ret:Expression = null
    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ProzedurAufruf"))
        ret = prozedurAufrufToExpression(son, prozedurname);
    }
    var f:functionExpression = ret.asInstanceOf[functionExpression]
    //TODO AProzedurAufruf
    return new AProzedurAufruf(f);
  }

  def bedingungenToBoolExpression(tree: SICTree, prozedurname: String) : BoolExpression = {
    var parts:ArrayList[BoolExpression] = new ArrayList();
    var art = 0;
    var notbed:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("notP")) {
        notbed = true;
      }
      else if(son.getValue().equals("Bedingungen")) {
        parts.add(bedingungenToBoolExpression(son, prozedurname));              
      }
      else if(son.getValue().equals("Bedingung")) {
        parts.add(bedingungToComparison(son, prozedurname));
      }
      else if(son.getValue().equals("Quantor")) {
        parts.add(quantorToQuantor(son, prozedurname));
      }
      else if(son.getValue().equals("LogiOp")) {
        nextson = son.getSons().elementAt(0) match {
          case x:SICTree => x
          case _ => null
        }

        if(nextson.getValue().equals("andP")) {
          art = 1;
        }
        else if(nextson.getValue().equals("orP")) {
          art = 0;
        }
      }   
    }
    
    parts.size() match {
      case 1 => 
        if(notbed == true)
          return new Not(parts.get(0));
        else
          return parts.get(0);
      case 2 =>
        if(notbed == true) {
          if(art == 1)
            return new Not(new AND(parts.get(0), parts.get(1)));
          else
            return new Not(new OR(parts.get(0), parts.get(1)));
        }
        else {
          if(art == 1)
            return new AND(parts.get(0), parts.get(1));
          else
            return new OR(parts.get(0), parts.get(1));
        }            
    }
    return null;
  }

  def bedingungToComparison(tree: SICTree, prozedurname: String) : Comparison = {
    var parts:ArrayList[Expression] = new ArrayList();
    var art = 0;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Ausdruck")) {
        parts.add(ausdruckToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("VergleichOp")) {
        nextson = son.getSons().elementAt(0) match {
          case x:SICTree => x
          case _ => null
        }

        if(nextson.getValue().equals("geP"))
          art = 0;                        
        else if(nextson.getValue().equals("gtP"))
          art = 1;  
        else if(nextson.getValue().equals("ltP"))
          art = 2;  
        else if(nextson.getValue().equals("leP"))
          art = 3;  
        else if(nextson.getValue().equals("neP"))
          art = 4;  
        else if(nextson.getValue().equals("eqP"))
          art = 5;  
      }
    }

    art match {
      case 0 => return new GOE(parts.get(0), parts.get(1));
      case 1 => return new Greater(parts.get(0), parts.get(1));
      case 2 => return new Lesser(parts.get(0), parts.get(1));
      case 3 => return new LOE(parts.get(0), parts.get(1));
      case 4 => return new Unequal(parts.get(0), parts.get(1));
      case 5 => return new Equal(parts.get(0), parts.get(1));
    }
    return null;
  }
  
  def quantorToQuantor(tree: SICTree, prozedurname: String) : Quantor = {
    var ret:Quantor = null;
    var sons = tree.getSons();
    var nextsons = tree.getSons();
    var parts:(String, ValueRange, BoolExpression) = null; 

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }
      nextsons = son.getSons();
      for(i <- 0 to nextsons.size()-1) {
        tmpson = nextsons.elementAt(i) match {         
          case x:SICTree => x;         
          case _ => null       
        }
        
        if(tmpson.getValue().equals("QuantorBedingungen")) {
          parts = quantorBedingungenToParts(tmpson, prozedurname);
        }
      }

      if(son.getValue().equals("AllQuantor")) {
        ret = new AllQuantor(parts._1, parts._2, parts._3);
      }
      else if(son.getValue().equals("ExQuantor")) {
        ret = new ExistsQuantor(parts._1, parts._2, parts._3);
      }
    }
    return ret;
  }

  def quantorBedingungenToParts(tree: SICTree, prozedurname: String) : (String, ValueRange, BoolExpression) = {
    var variable:String = null;
    var range:ValueRange = null;
    var bed:BoolExpression = null;
    var sons = tree.getSons();
    
    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }
      
      if(son.getValue().equals("Identifier")) {
        variable = identifierToString(son);
      }
      else if(son.getValue().equals("ArrayRange")) {
        range = arrayRangeToValueRange(son, prozedurname);
      }
      else if(son.getValue().equals("Bedingungen")) {
        bed = bedingungenToBoolExpression(son, prozedurname);
      }
    }
    return (variable, range, bed);
  }
  
  def ausdruckToExpression(tree: SICTree, prozedurname: String) : Expression = {
    return summeToExpression(tree, prozedurname);        
  }

  def summeToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var parts:ArrayList[Expression] = new ArrayList();
    var art = 0;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Produkt")) {
        parts.add(produktToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("Summe")) {
        parts.add(summeToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("AddsOp")) {
        nextson = son.getSons().elementAt(0) match {
          case x:SICTree => x
          case _ => null
        }

        if(nextson.getValue().equals("plusP"))
          art = 0;
        else
          art = 1;
      }
    }

    if(parts.size() == 1)
      return parts.get(0);
    else {
      if(art == 0)
        return new Plus(parts.get(0), parts.get(1));
      else
        return new Minus(parts.get(0), parts.get(1));
    }
  }

  def produktToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var parts:ArrayList[Expression] = new ArrayList();
    var art = 0;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Faktor")) {
        parts.add(faktorToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("Produkt")) {
        parts.add(produktToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("MultsOp")) {
        nextson = son.getSons().elementAt(0) match {
          case x:SICTree => x
          case _ => null
        }

        if(nextson.getValue().equals("multP"))
          art = 0;
        else if(nextson.getValue().equals("divP"))
          art = 1;
        else
          art = 2;
      }
    }

    if(parts.size() == 1)
      return parts.get(0);
    else {
      if(art == 0)
        return new Mult(parts.get(0), parts.get(1));
      else if(art == 1)
        return new Div(parts.get(0), parts.get(1));
      else
        return new Modulo(parts.get(0), parts.get(1));
    }
  }

  def faktorToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var parts:ArrayList[Expression] = new ArrayList();
    var art = 0;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Term")) {
        parts.add(termToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("Faktor")) {
        parts.add(faktorToExpression(son, prozedurname));
      }
    }

    if(parts.size() == 1)
      return parts.get(0);
    else {
      return new PowerOf(parts.get(0), parts.get(1));
    }
  }

  def termToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var ret:Expression = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Summe")) {
        ret = summeToExpression(son, prozedurname);
      }
      else if(son.getValue().equals("ExpWert")) {
        ret = expWertToExpression(son, prozedurname);
      }
    }

    return ret;
  }
  
  def allWertToExpression(tree:SICTree, prozedurname: String) : Expression = {
    var ret:Expression = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ArrayWerte")) {
        ret = arrayWerteToExpression(son, prozedurname);
      }
      else if(son.getValue().equals("ExpWert")) {
        ret = expWertToExpression(son, prozedurname);
      }
    }

    return ret;        
  }
  
  def arrayWerteToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var ret:ExpArray = null;
    var parts:ArrayList[Expression] = new ArrayList()
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("WertListe")) {
        parts = wertListeToArrayList(son, prozedurname);
      }
    }
    
    ret = new ExpArray(new IndexRange(0, parts.size()-1))
    for(i <- 0 to parts.size()-1)
      ret.setValue(i, parts.get(i))
    return ret;
  }
  
  def wertListeToArrayList(tree: SICTree, prozedurname: String) : ArrayList[Expression] = {
    var ret:ArrayList[Expression] = new ArrayList();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ExpWert")) {
        ret.add(expWertToExpression(son, prozedurname));
      }
      else if(son.getValue().equals("WerteListe"))
        ret.addAll(wertListeToArrayList(son, prozedurname))
    }
    return ret;
  }

  def expWertToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var ret:Expression = null;
    var negate:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier")) {
        ret = new Name(identifierToString(son));
      }
      else if(son.getValue().equals("IntWert")) {
        ret = intWertToExpression(son, prozedurname); //numberPToString(son).toInt)
      }
      else if(son.getValue().equals("ProzedurAufruf")) {
        nextson = son.getSons().elementAt(0) match {
          case x:SICTree => x
          case _ => null
        }

        ret = prozedurAufrufToExpression(son, prozedurname);
      }
      else if(son.getValue().equals("ExpWert")) {
        var ret2 = expWertToExpression(son, prozedurname)
        if(ret2.isInstanceOf[ewu.Number])
          return new Number(-ret2.asInstanceOf[ewu.Number].value)
        else
          return new Minus(new Number(BigInt(0)), ret2)
      }
    }
    return ret;        
  }
  
  def intWertToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var ret:Expression = null;
    var negate:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("ArrayKeyZugriff")) {
        ret = arrayKeyZugriffToExpression(son, prozedurname)
      }
      else if(son.getValue().equals("numberP")) {
        ret = new ewu.Number(BigInt(numberPToString(son).toInt));
      }
    }
    return ret; 
  }
  
  def arrayKeyZugriffToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var name:String = null;
    var first:Boolean = false;
    var last:Boolean = false;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {        
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier"))
        name = identifierToString(son);
      else if(son.getValue().equals("firstP"))
        first = true
      else if(son.getValue().equals("lastP"))
        last = true
    }
    if(first == true)
      return new ArrayKey(name, true, false)
    else
      return new ArrayKey(name, false, true)
  }

  def prozedurAufrufToExpression(tree: SICTree, prozedurname: String) : Expression = {
    var pname:Name = null;
    var expparts:ArrayList[Expression] = new ArrayList()
    var exps:Array[Expression] = null;
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {        
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("LinkedName"))
        pname = linkedNameToName(son);
      else if(son.getValue().equals("WertListe")) {
        expparts = wertListeToArrayList(son, prozedurname);
      }
    }
    exps = new Array(expparts.size)
    for(i <- 0 to expparts.size()-1)
      exps(i) = expparts.get(i)
    
    var ta = arrayvars.get(prozedurname) match {
      case Some(x) => x.contains(pname.name)
      case None => false
    }
    
    if(ta && exps.length == 1)
        return new ArrayAtIndex(pname.name, exps(0))
    else {
      var func = new functionExpression(pname.name, exps)

      var alist:ArrayList[functionExpression] = functionExecutes.get(prozedurname) match {
        case Some(x) => x
        case None => null
      }
      if(alist != null) {
        if(alist != null && !alist.contains(func))
          alist.add(func)
      }
      return func;
    }
  }

  def linkedNameToName(tree: SICTree) : Name = {
    var parts:ArrayList[String] = new ArrayList();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier"))
        parts.add(identifierToString(son));
    }

    if(parts.size() == 1)
      return new Name(parts.get(0));
    else
      return new Name(parts.get(0) + "." + parts.get(1));
  }

  def identifierListToArrayList(tree: SICTree) : ArrayList[String] = {
    var parts:ArrayList[String] = new ArrayList();
    var sons = tree.getSons();

    var son, nextson, tmpson : SICTree = null;
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }

      if(son.getValue().equals("Identifier"))
        parts.add(identifierToString(son));
      else if(son.getValue().equals("IdentifierList"))
        parts.addAll(identifierListToArrayList(son));
    }
    return parts;
  }

  def identifierToString(tree: SICTree) : String = {
    var sons = tree.getSons();
    var son:SICTree = null;
    
    for(i <- 0 to sons.size()-1) {
      son = sons.elementAt(i) match {         
        case x:SICTree => x;         
        case _ => null       
      }
      if(son.getValue().equals("identP")) {
       var ret = son.getValueOfAttribute(son.getAttributes()(0)).toString.replaceAll("\"", "") 
       return ret
      }
    }
    return "";
  }

  def numberPToString(tree: SICTree) : String = {
    return tree.getValueOfAttribute(tree.getAttributes()(0)).toString.replaceAll("\"", "")
  }
}
